package com.sakiko.audiobook.account.receiver;

import com.rabbitmq.client.Channel;
import com.sakiko.audiobook.account.service.UserAccountService;
import com.sakiko.audiobook.common.rabbit.constant.MqConst;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * projectName: com.sakiko.audiobook.account.receiver
 *
 * @author: sakiko
 * time: 2025/9/10 19:46
 * description:
 */
@Component
@Slf4j
public class AccountReceiver {

    @Autowired
    private UserAccountService userAccountService;

    /**
     * 监听消息;
     * 1、监听消息; 2、初始化队列、交换机、路由键三者之间的绑定关系;
     */
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_USER_REGISTER),
            exchange = @Exchange(value = MqConst.EXCHANGE_USER),
            key = MqConst.ROUTING_USER_REGISTER
    ))
    public void register(Long userId, Message message, Channel channel) {
        System.out.println("用户信息注册成功，发送消息给用户服务");
        // 如果消费者处理业务逻辑时，出现了异常，也要手动签收消息
        // 判断消息是否正确接收
        try {
            if (null != userId) {
                // 调用服务层方法，进行初始化账户金额
                userAccountService.initAccount(userId);
            }
        } catch (Exception e) {
            // 落盘到数据异常信息表; exMsg();
            log.error(e.getMessage());
            // 利用 redis记录重试次数
//               try {
//                channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,true);
//            } catch (IOException ex) {
//                throw new RuntimeException(ex);
//            }
//            return;
        }
        // 手动确认
        try {
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

/*
如何保证消息不丢失？
生产者:
    1、保证消息持久化
      a.队列持久化(durable)
      b.交换机持久化(durable)
      c.消息本身持久化(deliveryMode=2)
    2、开启确认模式(实现 applicationListener接口
     -- class RabbitInitConfigApplicationListener implements ApplicationListener<ApplicationReadyEvent>
      a.配置文件添加配置信息 publisher-confirm-type: correlated
                         publisher-returns: true
      b.消息正确到交换机 setConfirmCallback() -> false(没到) -> retry
      c.消息正确到队列 setReturnsCallback() -> 消息没到队列，走此方法 -> retry
      d.重试方法 retry() 超过若干次记录消息表，不超过阈值，convertAndSend重新发送
消费者:
    1、手动开启确认机制: spring.rabbitmq.listener.simple.acknowledge-mode=manual
    2、正确消费 使用 ack方法确认 channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
       消费失败 使用 nack方法确认 -> 是否重回队列？重试次数 or 记录到异常消息表中 exMsg表
 */
